home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
BBS in a Box 7
/
BBS in a Box - Macintosh - Volume VII (BBS in a Box) (January 1993).iso
/
Files
/
Tele
/
C
/
Comet2.1.3.cpt
/
Comet
/
tftp_srv.c
< prev
next >
Wrap
Text File
|
1991-05-06
|
12KB
|
524 lines
/* Copyright 1984 by the Massachusetts Institute of Technology */
/*
Copyright Cornell University 1986. All rights are reserved.
As of 4/10/86:
This source file may have no changes from the M.I.T original
other than this notice; but it has been tested as part of
Cornell's Aztec-C port. See notice.h
*/
/* See permission and disclaimer notice in file "notice.h" */
#include <notice.h>
#include "tftp.h"
extern int tntftp(), tntfdn();
extern int (*tfs_alert)();
extern int (*tfs_done)();
extern char macfile[]; /* for Macintosh file name translation */
extern int ntftps;
extern int tfstate;
extern long refusedt; /* time of most recent transfer refusal */
extern unsigned NDEBUG;
/* the tftp server code */
/* Modified 12/23/83 to upcall a "done" function when the transfer is
complete. Modified 1/11/84 to detect and discard duplicate requests
to open a tftp connection. Fixed file open calls to use only binary mode
(was making netascii transfers fail); fixed bug in counting number of
tftp connections in progress; changed calls to send error packets to
use incoming packet and socket number where appropriate, 1/13/84.
Added test to discard duplicate requests that match current connections,
that arrived before user refused a request or before discovery that
file can't be transferred.
<J. H. Saltzer>
1/24/84 - added octet mode which is the same as image mode.
<John Romkey>
3/21/84 - corrected several places in the code where cn->tf_expected
was referenced as cn->tf_expect and caused problems with the new
compiler.
<John Romkey>
4/12/84 - in tfsread() fixed call to udp_alloc() which was missing
the option length parameter.
<John Romkey>
12/21/84 - in tfsread() fixed buffer swapping code to prevent loss
of one packet buffer on each transfer.
<J. H. Saltzer>
*/
#define MAXTFTPS 1
UDPiopb tfspb; /* control block for TFTP listener socket */
char tfsbuf[MACUDPLEN]; /* MacTCP read buffer */
StreamPtr tfsstream; /* stream ptr for listener socket */
int tfs_read_req; /* server read requests */
int tfs_read_comp; /* server read completions */
int tfsdata; /* data has arrived */
struct tfconn tfsconn; /* tftp control connection struct */
struct tfconn tftpconn; /* tftp transfer connection struct */
UDPiopb tftppb; /* control block for TFTP transfer socket */
char tftpbuf[MACUDPLEN]; /* MacTCP read buffer */
StreamPtr tftpstream; /* stream ptr for transfer socket */
int tftp_read_req; /* server read requests */
int tftp_read_comp; /* server read completions */
int tftpdata; /* data has arrived */
extern char * malloc();
unsigned long getmyA5();
/* ioCompletion routine for the tftp listener on the server socket */
void tfs_read_done(udp_read)
UDPiopb *udp_read;
{
unsigned long oldA5;
oldA5 = getmyA5();
tfsdata = TRUE;
tfs_read_comp++;
setA5(oldA5);
}
/* ioCompletion routine for the tftp transfer socket */
void tftp_read_done(udp_read)
UDPiopb *udp_read;
{
unsigned long oldA5;
oldA5 = getmyA5();
tftpdata = TRUE;
tftp_read_comp++;
setA5(oldA5);
}
udp_release()
{
UDPiopb relPB;
OSErr errcode;
if (!tfsstream)
return(-1);
relPB.ioCRefNum = ipp_refnum;
relPB.csCode = UDPRelease;
relPB.udpStream = tfsstream;
if (errcode = PBControl(&relPB, (Boolean) FALSE))
error("udp release failed");
}
/* mtcp_tfsinit(alert, done) - initialize the tftp server. This opens a UDP
connection but does not turn on the server. That needs to done by
an explicit call to tfs_on().
alert() is a function which the server will call whenever it receives
request for a transfer. This function will be called int he following
way:
alert(ip_addr, file_name, direction)
alert() should return TRUE if it wishes to allow the transfer and
FALSE otherwise. done() is a function that the server will
call to inform the invoker that this file transfer is complete or
aborted.
*/
mtcp_tfsinit(alert, done)
int (*alert)();
int (*done)();
{
OSErr errcode;
extern short macipopen;
if (!macipopen) {
if (macip_init())
return(-1);
}
tfspb.ioCRefNum = ipp_refnum;
tfspb.csCode = UDPCreate;
tfspb.csParam.create.rcvBuff = &tfsbuf[0];
tfspb.csParam.create.rcvBuffLen = MACUDPLEN;
tfspb.csParam.create.notifyProc = (ProcPtr) udp_event;
tfspb.csParam.create.localPort = TFTPSOCK;
if (errcode = PBControl(&tfspb, (Boolean) FALSE)) {
if (errcode != duplicateSocket)
/* skip message if socket already opened */
error("tftp: UDPCreate failed");
return(-1);
}
tfsstream = tfspb.udpStream;
exit_hook(udp_release);
if (mttfmkcn(&tfsconn, PUT, ASCII)) {
/* make one connection block */
return(-1);
}
tfsconn.stream = tfsstream;
tfspb.csCode = UDPRead;
tfspb.ioCompletion = tfs_read_done;
tfspb.csParam.receive.timeOut = 0;
if (errcode = PBControl(&tfspb, (Boolean) TRUE)) {
error("tftp: no UDPRead");
}
tfs_read_req++;
tfs_done = done;
tfs_alert = alert;
return(FALSE);
}
/* handle an incoming tftp packet. This involves opening a udp connection
(immediately so that we can report errors). If the server is OFF
then the tftp will be refused; otherwise more checking will be done.
Call the alert function and verify that the "user" wishes to allow
the tftp. Report an error if not. Finally, spawn a task to oversee
the tftp and cleanup when it's done.
*/
ip_addr tftphost; /* to check for duplicate connection request */
udp_port tftpport; /* ditto */
mtcp_tfshnd(ptreq, len, host, port, time)
struct tfreq *ptreq;
unsigned len;
ip_addr host;
udp_port port;
unsigned short time;
{
unsigned nport;
struct tfconn *cn = &tftpconn;
char *file, *smode, *tmp;
unsigned mode;
OSErr errcode;
/* If there is already a connection like this one, ignore duplicate
request.
if (udp_ckcon(host, port)) {
return(0);
}
*/
/* since we don't have access to UDP with MacTCP we maintain this manually */
if (host == tftphost && port == tftpport) {
return(0);
}
/* If we refused a connection since this request got enqueued,
assume this is a duplicate and discard it, so we don't bother the
user with a duplicate question. (If we are unlucky, this might be
a request from somewhere else that arrived while the user was
thinking over the previous request. Tough; somewhere else will
just have to try again.)
*/
if (refusedt > time) {
return(0);
}
/* The next question: Do we have room to do this transfer? */
if (ntftps >= MAXTFTPS) {
mtcp_tfrpyerr(&tfsconn, ERRTXT, "Too many connections.");
return(0);
}
/* OK, let's check over the request more carefully. */
if (ptreq->tf_op > WRQ) {
#ifdef TFTPDEBUG
if (NDEBUG & INFOMSG|NETERR|PROTERR)
printf("TFTP SERVER: bad tftp opcode %u\n", ptreq->tf_op);
#endif
return(0);
}
file = &ptreq->tf_name[0];
smode = file + strlen(file) + 1;
for (tmp = smode; *tmp; tmp++)
if (*tmp >= 'A' && *tmp <= 'Z')
*tmp += 32;
if (strcmp(smode, "image") == 0)
mode = IMAGE;
else if (strcmp(smode, "octet") == 0)
mode = IMAGE;
else if (strcmp(smode, "netascii") == 0)
mode = ASCII;
else {
#ifdef TFTPDEBUG
if (NDEBUG & INFOMSG|NETERR|PROTERR)
printf("TFTP SERVER: Bad mode %s in req\n", smode);
#endif
mtcp_tfrpyerr(&tfsconn, ERRTXT, "Bad mode");
return(0);
}
if (tfstate == OFF) {
mtcp_tfrpyerr(&tfsconn, ERRTXT,"Transfers are not being accepted.");
return(0);
}
if ((*tfs_alert)(host, file, ptreq->tf_op == RRQ ? PUT : GET) == 0) {
mtcp_tfrpyerr(&tfsconn, ERRTXT, "Transfer refused.");
refusedt = time;
return(0);
}
/* It looks safe to try to open a connection. */
if (mttfmkcn(cn, PUT, ASCII)) {
/* Direction is a dummy for now */
error("TFTP SERVER: Can't make connection, ignoring request");
return(0);
}
#ifdef UDPFUNC
cn->tf_udp = udp_open(host, port, udp_socket(), tftp_read_done, cn);
if (cn->tf_udp == NULL) {
error("TFTP SERVER: Can't open udp connection, ignoring request");
mtcp_tfcleanup(cn);
return(0);
}
#else
tftppb.ioCRefNum = ipp_refnum;
tftppb.csCode = UDPCreate;
tftppb.csParam.create.rcvBuff = &tftpbuf[0];
tftppb.csParam.create.rcvBuffLen = MACUDPLEN;
tftppb.csParam.create.notifyProc = (ProcPtr) udp_event;
tftppb.csParam.create.localPort = udp_socket();
if (errcode = PBControl(&tftppb, (Boolean) FALSE)) {
error("tftp: couldn't open UDP");
}
cn->tf_udp = &tftppb;
cn->tf_host = host;
cn->tf_port = port;
tftpstream = tftppb.udpStream;
cn->stream = tftpstream;
/* and set up a read */
tftppb.csCode = UDPRead;
tftppb.ioCompletion = tftp_read_done;
tftppb.csParam.receive.timeOut = 0;
if (errcode = PBControl(&tftppb, (Boolean) TRUE)) {
error("tftp: no UDPRead");
}
#endif
cn->tf_mode = mode;
cn->tf_state = INIT;
macpath(file, &macfile[0]);
if (ptreq->tf_op == RRQ) {
cn->tf_dir = PUT;
cn->tf_haveport = TRUE;
cn->tf_fd = fopen(&macfile[0], "r");
if (cn->tf_fd == NULL) {
#ifdef TFTPDEBUG
if (NDEBUG & NETERR|PROTERR)
printf("couldn't open file\n");
#endif
mtcp_tfudperr(cn, FNOTFOUND, NULL);
refusedt = cticks;
mtcp_tfcleanup(cn);
(*tfs_done)(OFF);
return(0);
}
tfssend(cn);
}
else {
cn->tf_dir = GET;
cn->tf_haveport = TRUE;
cn->tf_fd = fopen(&macfile[0], "w");
if (cn->tf_fd == NULL) {
/* error("couldn't open file"); */
mtcp_tfudperr(cn, ACCESS, NULL);
refusedt = cticks;
mtcp_tfcleanup(cn);
(*tfs_done)(OFF);
return(0);
}
tfsrcv(cn);
}
ntftps++;
tftphost = host;
tftpport = port;
}
tfsrcv(cn)
struct tfconn *cn;
{
switch (cn->tf_state) {
case INIT: {
cn->tf_expected = 1;
mtcp_tfsndack(cn, 0);
cn->tf_state = DATAWAIT;
break;
}
case RCVDATA: {
cn->tf_state = DATAWAIT;
break;
}
case RCVLASTDATA: {
mtcp_tfcleanup(cn);
(*tfs_done)(ON);
ntftps--;
tftphost = 0;
tftpport = 0;
break;
}
case TIMEOUT: {
#ifdef TFTPDEBUG
if (NDEBUG & TMO)
printf("TFTP: Host not responding, giving up\n");
#endif
mtcp_tfudperr(cn, ERRTXT,
"Retry limit exceeded, giving up");
mtcp_tfcleanup(cn);
(*tfs_done)(OFF);
ntftps--;
tftphost = 0;
tftpport = 0;
break;
}
default: {
/* unknown state, kill it */
mtcp_tfcleanup(cn);
(*tfs_done)(OFF);
ntftps--;
tftphost = 0;
tftpport = 0;
break;
}
}
}
/* vars belong in conn struct */
int flen;
int tftpdone;
char * pfill;
char * psnd;
tfssend(cn)
struct tfconn *cn;
{
switch (cn->tf_state) {
case INIT: {
/* no connection, initialize */
flen = NORMLEN;
tftpdone = 0;
cn->tf_expected = 0;
psnd = cn->tf_outp; /* psnd is tftp_util's buffer */
pfill = malloc(NORMLEN); /* pfill is ours */
if (pfill == NULL) {
mtcp_tfudperr(cn, ERRTXT, "Couldn't allocate packet.");
#ifdef TFTPDEBUG
if (NDEBUG & NETERR|PROTERR)
printf("tfsread: couldn't allocate pfill\n");
#endif
mtcp_tfcleanup(cn);
(*tfs_done)(OFF);
}
/* fill up a data packet */
fillpacket(cn);
/* and fall through to send the first packet */
}
case RCVACK: {
cn->tf_expected++;
/* Check if we're done */
if (tftpdone) {
mtcp_tfcleanup(cn);
(*tfs_done)(ON);
free(pfill);
ntftps--;
tftphost = 0;
tftpport = 0;
break;
}
/* If not, resume the loop */
psnd = pfill; /* prepare to switch */
pfill = cn->tf_outp; /* take tftp_util's buffer */
cn->tf_outp = psnd; /* our buffer to tftp_util */
mtcp_tfsndata(cn, flen);
if (flen < NORMLEN)
tftpdone = 1;
else
fillpacket(cn); /* fill the next packet as we wait for ACK */
break;
}
case TIMEOUT: {
#ifdef TFTPDEBUG
if (NDEBUG & TMO)
printf("TFTP: Host not responding, giving up\n");
#endif
mtcp_tfudperr(cn,
ERRTXT, "Retry limit exceeded, giving up");
mtcp_tfcleanup(cn);
(*tfs_done)(OFF);
free(pfill);
ntftps--;
tftphost = 0;
tftpport = 0;
break;
}
default: {
/* something's wrong here */
mtcp_tfcleanup(cn);
(*tfs_done)(ON);
free(pfill);
ntftps--;
tftphost = 0;
tftpport = 0;
break;
}
}
}
fillpacket(cn)
struct tfconn *cn;
{
register char * datap;
register char * endp;
datap = (char *) tftp_data(pfill);
flen = fread(datap, 1, NORMLEN, cn->tf_fd);
if (cn->tf_mode == ASCII) {
/* translate CR's to LF's */
for (endp = datap + flen; datap < endp; datap++) {
if (*datap == 0x0d)
*datap = 0x0a;
}
}
cn->tf_size += flen;
}